package identical_scheduling_algorithm;


import ilog.concert.*;

import java.util.*;

import ilog.cplex.*;
import de.cau.apo.problems.identicalscheduling.Job;

public class LpSolver {
   static double limit = 1.0e-6;
   
   // Data of the problem
    double   binSize;
    double[] jobSize;
    double[] amountPerJob;
    double delta;

   
   public LpSolver (double delta, double makeSpan, Job [] jobs) {
     
   
	   binSize =1;
	   
	   this.delta = delta;
	
      // Number of Jobs 
	   RoundJobs rj = new RoundJobs();
      amountPerJob    = rj.roundJobs(delta, makeSpan, jobs);
      
      
      // Rounded Value p_j
      jobSize      =  new double[amountPerJob.length];
      
      System.out.println("Große gerundete Jobs:");
      for(int i =0; i< jobSize.length; i++){
    	  jobSize[i]= delta* Math.pow((delta + 1), i);
    	  System.out.println("Job " +  (i+1) + " mit Jobgroesse "+ jobSize[i]+  " und Anzahl " +amountPerJob[i]);
      }
      System.out.println();
   }
   


   static void printConfig( ArrayList<ArrayList<Integer>> matrix, ArrayList<Double> solution ){
	   for(int i = 0; i < matrix.size(); i++){
		   for(int j = 0; j < matrix.get(i).size(); j++)
			   System.out.print(matrix.get(i).get(j)+ ", ");
		   System.out.print( "    x"+ i+ " = "+ solution.get(i));
		   System.out.println();
	   }
   }

   static class IloNumVarArray {
      int _num           = 0;
      IloNumVar[] _array = new IloNumVar[32];

      void add(IloNumVar ivar) {
         if ( _num >= _array.length ) {
            IloNumVar[] array = new IloNumVar[2 * _array.length];
            System.arraycopy(_array, 0, array, 0, _num);
            _array = array;
         }
         _array[_num++] = ivar;
      }

      IloNumVar getElement(int i) { return _array[i]; }
      int       getSize()         { return _num; }
   }

   public  Cofiguration lpSolver() throws IloException {
	   
	   // Configuration-Matrix, used for cutting
	   ArrayList<ArrayList<Integer>> matrix = new ArrayList<ArrayList<Integer>>();
	   ArrayList<Double> solution = new ArrayList<Double>();
	   
                
         IloCplex solver = new IloCplex();
       
         IloObjective binsUsed = solver.addMinimize();
         IloRange[]   Fill = new IloRange[amountPerJob.length];
         for (int f = 0; f < amountPerJob.length; f++ ) {
            Fill[f] = solver.addRange(amountPerJob[f], Double.MAX_VALUE);
            
         }
         IloNumVarArray a = new IloNumVarArray();
       
         int nWdth = amountPerJob.length;
         
         System.out.println("Startmatrix: ");

         for (int j = 0; j < nWdth; j++){
            a.add(solver.numVar(solver.column(binsUsed, 1.0).and(
                                     solver.column(Fill[j],
                                                      (int)(binSize/jobSize[j]))),
                                     0.0, Double.MAX_VALUE));
            
            ArrayList<Integer> column = new ArrayList<Integer>();
            for(int i= 0; i< nWdth; i++){
            	if(i == j)
            		column.add((int)(binSize/jobSize[j]));
            	else
            		column.add(0);            	
            }
            matrix.add(column);
            
            for(int value: column)
            	System.out.print(value);
            System.out.println();
         }       

         solver.setParam(IloCplex.IntParam.RootAlg, IloCplex.Algorithm.Primal);
       
         // Pattern-Generation Problem
       
         IloCplex patternGenerationSolver = new IloCplex();
       
         IloObjective ReducedCost = patternGenerationSolver.addMinimize();
         IloNumVar[] Use = patternGenerationSolver.numVarArray(nWdth, 
                                                 0., Double.MAX_VALUE, 
                                                 IloNumVarType.Int);
         patternGenerationSolver.addRange(-Double.MAX_VALUE, 
                            patternGenerationSolver.scalProd(jobSize, Use),
                            binSize);
       
         // Column-Generation
       
         double[] newPatt = new double[nWdth];
       
       
          while (true) {
     

           solver.solve();
          
            // Find and add new Pattern
          
            double[] price = solver.getDuals(Fill);
            ReducedCost.setExpr(patternGenerationSolver.diff(1.,
                                               patternGenerationSolver.scalProd(Use, price)));
          
            patternGenerationSolver.solve();
          
            if ( patternGenerationSolver.getObjValue() > -limit )
               break;
          
            
            newPatt = patternGenerationSolver.getValues(Use);
            
            
            // Save a Column to the Configuration-Matrix
            ArrayList<Integer> spalte = new ArrayList<Integer>();
            for(int i =0; i <newPatt.length; i++)
            	spalte.add((int)newPatt[i]);
            matrix.add(spalte);
           
            IloColumn column = solver.column(binsUsed, 1.);
            for ( int p = 0; p < newPatt.length; p++ )
               column = column.and(solver.column(Fill[p], newPatt[p]));
            
            a.add( solver.numVar(column, 0., Double.MAX_VALUE) );
         }
         
         for(int i= 0;i < a.getSize();i++)
        	 solution.add(solver.getValue(a.getElement(i)));
         
     
       
         System.out.println("Solution status: " + solver.getStatus());       
         solver.end();
         patternGenerationSolver.end();
         printConfig(matrix,solution);
         System.out.println();
         System.out.println("Große gerundete Jobs:");
         for(int i =0; i< jobSize.length; i++){
       	  jobSize[i]= delta* Math.pow((delta + 1), i);
       	  System.out.println("Job " +  (i+1) + " mit Jobgröße "+ jobSize[i]+  " und Anzahl " +amountPerJob[i]);
         }
         return new Cofiguration(matrix,solution);
      
   }
}



